/**
 * 
 */
package org.jenkon;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.jenkon.io.utils.FilenameUtils;
import org.jenkon.utils.SystemUtils;

/**
 * Searcher for searching class at a spezific {@link File}. If the {@link File} is not a Directory,
 * then return a empty {@link List}.
 * 
 * @author Jens Konopka
 * 
 */
public class FileClassScanner extends AbstractClassScanner {

	/**
	 * 
	 */
	private static final long serialVersionUID = -2928305148411316295L;

	private final List<File> files = new ArrayList<File>();

	/**
	 * @param files Files to search
	 */
	public FileClassScanner(final File... files) {
		this(Arrays.asList(files));
	}

	/**
	 * @param files {@link List} with {@link File} objects to search
	 */
	public FileClassScanner(final List<File> files) {
		this.files.addAll(files);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jenkon.commons.lang.ClassSearcher#search(java.lang.String)
	 */
	public List<Class<?>> scan(final String packageName) {
		final List<Class<?>> classes = new ArrayList<Class<?>>();
		for (final File file : files) {
			final File directory = new File(FilenameUtils.concat(file.getPath(),
					packageName.replace(".", SystemUtils.getFileSeparator())));
			if (directory.isDirectory()) {
				classes.addAll(getClasses(true, directory, packageName));
			}
		}
		return classes;
	}

	/**
	 * @param first is the first call of method, then create the starting method name
	 * @param file the {@link File} of current flat
	 * @param packageName the name of the package
	 * @return {@link List} with classes which was found
	 */
	private List<Class<?>> getClasses(final boolean first, final File file, String packageName) {

		final List<Class<?>> classes = new ArrayList<Class<?>>();

		if (file.isDirectory()) {

			if (!first) {
				packageName = changePackageName(file, packageName);
			}

			for (String subFileOrDir : file.list()) {
				classes.addAll(getClasses(false, new File(file, subFileOrDir), packageName));
			}

		} else if (isClass(file)) {

			try {
				classes.add(getClass(file, packageName));
			} catch (Throwable ex) {
			}

		}

		return classes;
	}

	/**
	 * @param file the {@link File}
	 * @param packageName the name of the package
	 * @return the created name of the package from parameter {@link File} and packageName
	 */
	private String changePackageName(final File file, final String packageName) {
		return (packageName + "." + file.getName()).replaceAll("^\\.", "");
	}

	/**
	 * Check the {@link File} is this a {@link Class} or not.
	 * 
	 * @param file {@link File} to check of is class
	 * @return <code>true</code> if the {@link File} is a class, or <code>false</code> if the
	 *         {@link File} is not a class.
	 */
	private boolean isClass(final File file) {
		return file.getName().toLowerCase().endsWith(".class");
	}

	/**
	 * @param file the {@link File} to create the {@link Class}
	 * @param packageName the name of the package from {@link Class}
	 * @return the created {@link Class}
	 * @throws ClassNotFoundException if the {@link Class} was not found
	 */
	private Class<?> getClass(final File file, final String packageName) throws ClassNotFoundException {
		return Class.forName(packageName + "."
				+ file.getName().substring(0, file.getName().length() - ".class".length()));
	}

}
